package com.hb.trade.api.routine.controller.project.order;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.hb.core.pojo.enums.FeeTypeEnum;
import com.hb.core.pojo.enums.PayStatusEnum;
import com.hb.core.pojo.project.order.dto.UserOrderDTO;
import com.hb.core.pojo.project.order.entity.UserOrder;
import com.hb.core.pojo.project.section.dto.BidSectionDTO;
import com.hb.core.pojo.project.section.entity.BidSection;
import com.hb.trade.api.routine.config.pay.*;
import com.hb.trade.api.routine.service.PayService;
import com.hb.trade.client.project.SectionClient;
import com.hb.trade.client.project.UserOrderClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.fzzn.core.model.enums.AuditStatusEnum;
import org.fzzn.core.model.msg.Message;
import org.fzzn.core.model.msg.ResponseDto;
import org.fzzn.core.util.Enums;
import org.fzzn.core.util.IdUtil;
import org.fzzn.core.util.ResultUtil;
import org.fzzn.core.web.anno.Version;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static com.hb.trade.api.routine.config.pay.AliPayConfig.*;
import static com.hb.trade.api.routine.config.pay.WeixinPayConfig.SUCCESS_RESULT;

/**
 * 订单信息 前端控制器
 *
 * @author zhaojk
 * @since 2021-08-30
 */
@Slf4j
@Version(1.0)
@Api(tags = {"第三方支付接口"})
@RestController
@RequestMapping("/{version}")
public class ApiPayController {

    protected final PayService payService;
    protected final SectionClient sectionClient;
    protected final UserOrderClient userOrderClient;

    public ApiPayController(PayService payService,
                            SectionClient sectionClient,
                            UserOrderClient userOrderClient) {
        this.payService = payService;
        this.sectionClient = sectionClient;
        this.userOrderClient = userOrderClient;
    }

    /**
     * @Description: 支付宝回调
     * @Author: zhongdl
     * @update zhaojk
     * @Date:2021/12/23 10:02
     * @return: {@link ResponseDto }
     **/
    @RequestMapping("/notify/userOrder/zhiFuBaoCallback")
    public String zhiFuBaoCallback(AlipayNotifyResponse response, HttpServletRequest request) throws Exception {
        log.info("支付成功, 进入异步通知接口...");

        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            params.put(name, valueStr);
        }

        //调用SDK验证签名
        boolean signVerified = AlipaySignature.rsaCheckV1(params, PaymentConfig.ALI_ALIPAY_PUBLIC_KEY, CHARSET, response.getSign_type());

        //——请在这里编写您的程序（以下代码仅作参考）——

   /* 实际验证过程建议商户务必添加以下校验：
   1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
   2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
   3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）
   4、验证app_id是否为该商户本身。
   */
        //验证成功
        if (signVerified) {
            // 商户订单号
            String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");

            //支付宝交易号
            String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"), "UTF-8");

            //交易状态
            String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

            //付款金额
            String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"), "UTF-8");

            if (trade_status.equals("TRADE_FINISHED")) {
                //判断该笔订单是否在商户网站中已经做过处理
                //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                //如果有做过处理，不执行商户的业务程序

                //注意： 尚自习的订单没有退款功能, 这个条件判断是进不来的, 所以此处不必写代码
                //退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
            } else if (trade_status.equals("TRADE_SUCCESS")) {
                //判断该笔订单是否在商户网站中已经做过处理
                //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                //如果有做过处理，不执行商户的业务程序

                //注意：
                //付款完成后，支付宝系统发送该交易状态通知
                Message<UserOrder> userOrderMsg = userOrderClient.getOrder(out_trade_no);
                if (userOrderMsg.isFail()) {
                    return "fail";
                }
                UserOrder userOrder = userOrderMsg.getData();

                // 已支付成功的订单直接返回
                Integer orderStatus = userOrder.getOrderStatus();
                if (Enums.equals(PayStatusEnum.PAY_SUCCESS, orderStatus)) {
                    return "success";
                }

                userOrder.setOutOrderNo(out_trade_no);
                userOrder.setTradeNo(trade_no);
                userOrder.setPayTime(response.getGmt_payment());
                userOrder.setPayAccount(response.getBuyer_logon_id());
                userOrder.setAuditStatus(AuditStatusEnum.PASSED.getCode());
                userOrder.setOrderStatus(PayStatusEnum.PAY_SUCCESS.getCode());
                Message<UserOrderDTO> message = userOrderClient.updateOrderStatus(userOrder.to(UserOrderDTO.class));
                if (message.isSuccess()) {
                    payService.paySuccessOperations(message.getData());
                }

                log.info("********************** 支付成功(支付宝异步通知) **********************");
                log.info("* 订单号: {}", out_trade_no);
                log.info("* 支付宝交易号: {}", trade_no);
                log.info("* 实付金额: {}", total_amount);
                log.info("* 购买产品: {}", response.getSubject());
                log.info("***************************************************************");
            }
            log.info("支付成功...");
        } else {//验证失败
            log.info("支付, 验签失败...");
        }
        return "success";
    }

    /**
     * @Description: 微信支付回调
     * @Author: zhongdl
     * @update zhaojk
     * @Date:2021/12/23 10:02
     * @return: {@link ResponseDto }
     **/
    @RequestMapping("/notify/userOrder/weChatCallback")
    public String weChatCallback(@RequestBody String notifyInfo) {
        WxPayOrderNotifyResult notifyResult = WxPayOrderNotifyResult.fromXML(notifyInfo, WxPayOrderNotifyResult.class);

        // TODO 后期添加验签逻辑
/*        WXPayConfig config = new WXPayMyConfig(appId, mchId, key);
        WXPay wxpay = new WXPay(config);

        Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);  // 转换成map
        JSONObject notifyJSON = JSONUtil.createObj(); // 转成JSONObject使用方便
        notifyJSON.putAll(notifyMap);

        String outTradeNo = notifyJSON.getStr("out_trade_no");
        if (!wxpay.isPayResultNotifySignatureValid(notifyMap)) {
            //回调验签失败
            return "error";
        }*/
        // 扣款成功标识
        String returnCode = notifyResult.getReturnCode(); // 通信标识
        String resultCode = notifyResult.getResultCode(); // 业务结果(这项才能表达交易成功的意思)
        if ("SUCCESS".equals(resultCode) && "SUCCESS".equals(returnCode)) {
            //执行业务操作
            Message<UserOrder> userOrderMsg = userOrderClient.getOrder(notifyResult.getOutTradeNo());
            if (userOrderMsg.isFail()) {
                return "fail";
            }

            UserOrder userOrder = userOrderMsg.getData();

            // 已支付成功的订单直接返回
            Integer orderStatus = userOrder.getOrderStatus();
            if (Enums.equals(PayStatusEnum.PAY_SUCCESS, orderStatus)) {
                return SUCCESS_RESULT;
            }

            // 分转换成元 start
            Integer totalFee = notifyResult.getTotalFee();
            double div = NumberUtil.div(NumberUtil.toDouble(totalFee), 100D);
            userOrder.setActualAmount(BigDecimal.valueOf(div));
            // 分转换成元 start

            userOrder.setOutOrderNo(notifyResult.getOutTradeNo());

            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
            LocalDateTime localTime = LocalDateTime.parse(notifyResult.getTimeEnd(),df);
            userOrder.setPayTime(localTime);
            userOrder.setTradeNo(notifyResult.getTransactionId());
            userOrder.setOrderStatus(PayStatusEnum.PAY_SUCCESS.getCode());
            userOrder.setAuditStatus(AuditStatusEnum.PASSED.getCode());

            Message<UserOrderDTO> updateResultMsg = userOrderClient.updateOrderStatus(userOrder.to(UserOrderDTO.class));
            if (updateResultMsg.isSuccess()) {
                // 增加一条购标记录
                payService.paySuccessOperations(updateResultMsg.getData());
                return SUCCESS_RESULT;
            }
        }
        return null;
    }

    /**
     * 支付宝支付二维码
     *
     * @return
     * @throws AlipayApiException
     */
    @ApiOperation(value = "支付宝支付二维码", notes = "支付宝支付二维码", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "sectionId", value = "标段ID", dataTypeClass = Long.class),
            @ApiImplicitParam(name = "feeType", value = "费用类型", dataTypeClass = String.class),
            @ApiImplicitParam(name = "outOrderNo", value = "订单号", dataTypeClass = String.class),
            @ApiImplicitParam(name = "actualAmount", value = "订单金额(单位: 元)", dataTypeClass = String.class)
    })
    @RequestMapping("/alipay/getPayQrCode")
    public ResponseDto getPayQrCode(@RequestBody WeixinPayBody payBody) throws AlipayApiException {
        BidSectionDTO sectionDTO = new BidSectionDTO();
        sectionDTO.setSectionId(payBody.getSectionId());
        Message<BidSection> info = sectionClient.info(sectionDTO);

        FeeTypeEnum feeTypeEnum = Enums.of(FeeTypeEnum.class, payBody.getFeeType());

        AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, PaymentConfig.ALI_APP_ID, PaymentConfig.ALI_RSA_PRIVATE_KEY,
                FORMAT, CHARSET, PaymentConfig.ALI_ALIPAY_PUBLIC_KEY, SIGN_TYPE);

        JSONObject params = JSONUtil.createObj();

        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setNotifyUrl(AliPayConfig.notifyUrl()); // 异步通知接口
        request.setReturnUrl(AliPayConfig.notifyPage()); // 同步通知地址

        params.set("out_trade_no", payBody.getOutOrderNo());
        params.set("product_code", "FAST_INSTANT_TRADE_PAY");
        params.set("total_amount", payBody.getActualAmount());
//        params.set("total_amount", "0.01");
        params.set("subject", info.getData().getSectionName() + " - " + feeTypeEnum.getShowName());
        params.set("body", "订单详细介绍");
        params.set("passback_params", "一个参数");

        request.setBizContent(params.toString()); // 填充业务参数
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request); //调用SDK生成表单

//        return response.getBody();
        String body = response.getBody();
        return ResultUtil.msg(true, body);

        // 移除<script>标签
        // 前端又要加上
//        String sub = StrUtil.subBetween(body, "<script>", "</script>");
//        String replace = StrUtil.replace(body, "<script>" + sub + "</script>", "");
//        return ResultUtil.msg(true, replace);
    }

    /**
     * 微信支付二维码
     *
     * @param payBody
     * @return
     */
    @ApiOperation(value = "微信支付二维码", notes = "微信支付二维码", httpMethod = "POST")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "sectionId", value = "标段ID", dataTypeClass = Long.class),
            @ApiImplicitParam(name = "feeType", value = "费用类型", dataTypeClass = String.class),
            @ApiImplicitParam(name = "orderNo", value = "订单号", dataTypeClass = String.class),
            @ApiImplicitParam(name = "actualAmount", value = "订单金额(单位: 元)", dataTypeClass = String.class)
    })
    @PostMapping("/weixin/pay/pc")
    public ResponseDto pc(@RequestBody WeixinPayBody payBody) {
        BidSectionDTO sectionDTO = new BidSectionDTO();
        sectionDTO.setSectionId(payBody.getSectionId());
        Message<BidSection> info = sectionClient.info(sectionDTO);

        FeeTypeEnum feeTypeEnum = Enums.of(FeeTypeEnum.class, payBody.getFeeType());

        payBody.setBody(info.getData().getSectionName() + " - " + feeTypeEnum.getShowName());

        // 订单价格
        String totalFee = payBody.getActualAmount();
        double parseDouble = NumberUtil.parseDouble(totalFee);
        // 微信价格单位是分
        int amount = BigDecimal.valueOf(parseDouble).multiply(BigDecimal.valueOf(100L)).intValue();

        String outTradeNo = payBody.getOutOrderNo();
        // 生成预付单对象
        PreOrder preOrder = new PreOrder();
        // 生成随机字符串
        String nonce_str = IdUtil.simpleUUID();

        preOrder.setAppid(PaymentConfig.WX_APP_ID);
        preOrder.setMch_id(PaymentConfig.WX_MCH_ID);
        preOrder.setBody(payBody.getBody());
        preOrder.setNonce_str(nonce_str);
        preOrder.setOut_trade_no(outTradeNo);
        preOrder.setTotal_fee(amount);
//        preOrder.setTotal_fee(1);
        preOrder.setSpbill_create_ip(WeixinPayConfig.SPBILL_CREATE_IP);
        preOrder.setNotify_url(WeixinPayConfig.notifyUrl());
        preOrder.setTrade_type(WeixinPayConfig.TRADE_TYPE);

        SortedMap<Object, Object> params = new TreeMap<>();
        params.put("appid", preOrder.getAppid());
        params.put("mch_id", preOrder.getMch_id());
        params.put("body", preOrder.getBody());
        params.put("nonce_str", preOrder.getNonce_str());
        params.put("out_trade_no", preOrder.getOut_trade_no());
        params.put("total_fee", preOrder.getTotal_fee());
        params.put("spbill_create_ip", preOrder.getSpbill_create_ip());
        params.put("notify_url", preOrder.getNotify_url());
        params.put("trade_type", preOrder.getTrade_type());

        // 获得签名
        String sign = Sign.createSign("utf-8", params, PaymentConfig.WX_PAY_KEY);
        preOrder.setSign(sign);
        // Object转换为XML
        String xml = XmlUtil.toStr(XmlUtil.mapToXml(JSONUtil.parseObj(preOrder), "xml"), true);
        // 统一下单地址
        String url = WeixinPayConfig.PLACEANORDER_URL;
        // 调用微信统一下单地址
        String returnXml = HttpUtil.sendPost(url, xml);

        WeixinPreOrderResult result =
                XmlUtil.xmlToBean(XmlUtil.parseXml(returnXml).getElementsByTagName("xml").item(0),
                        WeixinPreOrderResult.class);
        if ("SUCCESS".equals(result.getResult_code()) && "SUCCESS".equals(result.getReturn_code())) {
            return ResultUtil.msg(true, result.getCode_url());
        }
        return ResultUtil.fail(result.toString());
    }

}
